#include "graphicsobject.h"
using namespace EPOC;

GraphicsObject::GraphicsObject()
{
	SetClassID(kCLASSID_GRAPHICSOBJECT);

	SetTexture(NULL);
	SetZOrder(ZORDER_MIN);
	SetVisible(true);
	SetPivotCentered(true);
	SetCullingEnabled(true);
	
	SetColorKey(ColorRGB(255, 0, 255));
	SetAlpha(255);
	SetPresetBlendMode(kBLENDMODE_PRESET_ALPHAMASK);
	SetAlphaRejection(0.5f);
}

GraphicsObject::~GraphicsObject()
{
	GraphicsObject::Cleanup();
}

void GraphicsObject::Cleanup()
{
	if(GetTexture())
		GetTexture()->Release();

	SetTexture(NULL);
}

bool GraphicsObject::Initialize(Texture *pTexture/*, RENDER_MODE nRenderMode*/)
{
	SetTexture(pTexture);

	//m_nRenderMode = nRenderMode;

	return true;
}


void GraphicsObject::Render()
{
	/*switch(GetRenderMode())
	{
		case kRENDER_MODE_SDL:
			RenderSDL(pDestTexture);
		break;

		case kRENDER_MODE_OPENGL:
			RenderOGL(pDestTexture);
		break;
	}*/
	RenderOGL();
}

void GraphicsObject::RenderSDL(TextureSDL* pDestTexture)
{
	if( !GetTexture())
		return;

	TextureSDL *pTexture = (TextureSDL*) GetTexture();

	if(IsAlphaEnabled())
		SDL_SetSurfaceAlphaMod(pTexture->GetSDLSurface(),  m_nAlpha);
	else
		SDL_SetSurfaceAlphaMod(pTexture->GetSDLSurface(), SDL_ALPHA_OPAQUE);

	if(IsColorkeyEnabled())
		SDL_SetColorKey(pTexture->GetSDLSurface(), SDL_TRUE | SDL_RLEACCEL, SDL_MapRGB(pTexture->GetSDLSurface()->format, m_rColorKey.m_nRed, m_rColorKey.m_nGreen, m_rColorKey.m_nBlue));
	else
		SDL_SetColorKey(pTexture->GetSDLSurface(), 0, 0);

	int nWidth = pTexture->GetDimension()->m_nWidth;
	int nHeight = pTexture->GetDimension()->m_nHeight;

	SDL_Rect rSource;
	rSource.x = 0;
	rSource.y = 0;
	rSource.w = (Uint16)(nWidth);
	rSource.h = (Uint16)(nHeight);

	SDL_Rect rDest;
	if(IsPivotCentered())
	{
		rDest.x = (Sint16)(GetPos()->m_fX - ((float)nWidth * 0.5f) + 0.5f);
		rDest.y = (Sint16)(GetPos()->m_fY - ((float)nHeight * 0.5f) + 0.5f);
	}
	else
	{
		rDest.x = (Sint16)(GetPos()->m_fX + 0.5f);
		rDest.y = (Sint16)(GetPos()->m_fY + 0.5f);
	}
	rDest.w = (Uint16)(nWidth);
	rDest.h = (Uint16)(nHeight);

	SDL_BlitSurface(pTexture->GetSDLSurface(), &rSource, pDestTexture->GetSDLSurface(), &rDest);
}


void GraphicsObject::RenderOGL()
{

	if( !GetTexture())
		return;

	if(IsAlphaEnabled())
	{
		glEnable(GL_ALPHA_TEST);
		glAlphaFunc(GL_GEQUAL, GetAlphaRejection());
	
		glEnable(GL_BLEND);
		glBlendFunc(m_nBlendFactorSrc, m_nBlendFactorDest);
	}
	else
	{
		glDisable(GL_ALPHA_TEST);

		glEnable(GL_BLEND);
		glBlendFunc(GL_ONE, GL_ZERO);
	}

	// TODO: do colorkeying
	//glEnable(GL_COLOR_MATERIAL);
	//glColorMaterial(GL_FRONT, GL_DIFFUSE);


	TextureOGL *pTexture = (TextureOGL*) GetTexture();
	
	// Bind the texture to which subsequent calls refer to
    glEnable(GL_TEXTURE_2D);
	
	glBindTexture( GL_TEXTURE_2D, pTexture->GetGLTexture() );
	glPushMatrix();

		float fZ = 1.0f * (float)GetZOrder()/(float)ZORDER_MAX;
		//float fZ = (float)GetZOrder();
		//float fZ = 0.0f;

		glTranslatef(GetPos()->m_fX, GetPos()->m_fY, fZ);
		glRotatef(GetRot(), 0.0f, 0.0f, 1.0f);
		glScalef(GetScl()->m_fX, GetScl()->m_fY, 1.0f);

		//float fRatioY = (float)pTexture->GetDimension()->m_nHeight / (float)pTexture->GetDimension()->m_nWidth;
		
		float fHalfHeight = ((float)pTexture->GetDimension()->m_nHeight) * 0.5f * (m_rUVdata.m_fVEnd - m_rUVdata.m_fVStart);
		float fHalfWidth = ((float)pTexture->GetDimension()->m_nWidth) * 0.5f * (m_rUVdata.m_fUEnd - m_rUVdata.m_fUStart);

		float fOffsetX = 0.0f;
		float fOffsetY = 0.0f;

		if(!IsPivotCentered())
		{
			fOffsetX = fabsf(fHalfWidth);
			fOffsetY = fabsf(fHalfHeight);
		}
		
		glBegin( GL_QUADS );
			// Top-left vertex (corner)
			glTexCoord2f( m_rUVdata.m_fUStart, m_rUVdata.m_fVStart );
			glVertex3f( -fHalfWidth + fOffsetX, -fHalfHeight + fOffsetY, 0 );

			// Top-right vertex (corner)
			glTexCoord2f(m_rUVdata.m_fUEnd, m_rUVdata.m_fVStart );
			glVertex3f( fHalfWidth + fOffsetX, -fHalfHeight + fOffsetY, 0 );

			// Bottom-right vertex (corner)
			glTexCoord2f(m_rUVdata.m_fUEnd, m_rUVdata.m_fVEnd );
			glVertex3f( fHalfWidth + fOffsetX, fHalfHeight + fOffsetY, 0 );
	    
			// Bottom-left vertex (corner)
			glTexCoord2f( m_rUVdata.m_fUStart, m_rUVdata.m_fVEnd );
			glVertex3f( -fHalfWidth + fOffsetX, fHalfHeight + fOffsetY, 0 );	
		glEnd();
		
	glPopMatrix();
}

ColorRGBA GraphicsObject::GetPixelColorAt(int nX, int nY)
{
	ColorRGBA rPixelColor;
	//switch(GetRenderMode())
	//{
	//	case kRENDER_MODE_SDL:
	//		rPixelColor = GetPixelColorAtSDL(nX, nY);
	//	break;

	//	case kRENDER_MODE_OPENGL:
	//		rPixelColor = GetPixelColorAtOGL(nX, nY);
	//	break;
	//}

	return rPixelColor;
}

ColorRGBA GraphicsObject::GetPixelColorAtSDL(int nX, int nY)
{
	// create color data to fill and return
	ColorRGBA rColor;

	// check if there's a surface and if it is an sdl surface
	if(!GetTexture())
	{
		fprintf(stderr, "Error - GraphicsObject::GetPixelColorAt: No Surface or no SDL surface in surface object: \n");
		return rColor;
	}

	// check if there's any data in our surface
	TextureSDL *pTexture = (TextureSDL*) GetTexture();
	if(!pTexture->GetSDLSurface())
		return rColor;

	// check if coordinates is out of bounds
	if(nX < 0 || nX >= pTexture->GetDimension()->m_nWidth || nY < 0 || nY >= pTexture->GetDimension()->m_nHeight)
		return rColor;


	// Lock surface if needed
	if( SDL_MUSTLOCK( pTexture->GetSDLSurface()))
	{
		if( SDL_LockSurface( pTexture->GetSDLSurface()) < 0 )
		{
			fprintf(stderr, "Error - GraphicsObject::GetPixelColorAt: Surface '%s' could not be locked: %s\n", pTexture->GetFilePath().c_str(), SDL_GetError());
			return rColor;
		}
	}

	
	// get bytes per pixel
	Uint32 nPixelValue = 0;
	int bpp = pTexture->GetSDLSurface()->format->BytesPerPixel;

	/* Here p is the address to the pixel we want to retrieve */
	Uint8 *p = (Uint8 *)pTexture->GetSDLSurface()->pixels + nY * pTexture->GetSDLSurface()->pitch + nX * bpp;

	switch(bpp) 
	{
		case 1:
			nPixelValue = *p;
		break;

		case 2:
			nPixelValue = *(Uint16 *)p;
		break;

		case 3:
			if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
				nPixelValue = p[0] << 16 | p[1] << 8 | p[2];
			else
				nPixelValue = p[0] | p[1] << 8 | p[2] << 16;
		break;

		case 4:
			nPixelValue = *(Uint32 *)p;
		break;

		default:
			nPixelValue = 0;
		break;
	}
			
	// Unlock if needed
	if( SDL_MUSTLOCK( pTexture->GetSDLSurface())) 
		SDL_UnlockSurface( pTexture->GetSDLSurface());
		
	SDL_GetRGBA(nPixelValue, pTexture->GetSDLSurface()->format, &rColor.m_nRed, &rColor.m_nGreen, &rColor.m_nBlue, &rColor.m_nAlpha);

	return rColor;

}


ColorRGBA GraphicsObject::GetPixelColorAtOGL(int nX, int nY)
{
	// create color data to fill and return
	ColorRGBA rColor;

	// check if there's a surface and if it is an sdl surface
	if(!GetTexture())
	{
		fprintf(stderr, "Error - GraphicsObject::GetPixelColorAt: No Surface or no SDL surface in surface object: \n");
		return rColor;
	}

	// check if there's any data in our surface
	TextureOGL *pTexture = (TextureOGL*) GetTexture();
	if(!pTexture->IsEditable() || !pTexture->GetSDLSurface())
		return rColor;

	// check if coordinates is out of bounds
	if(nX < 0 || nX >= pTexture->GetDimension()->m_nWidth || nY < 0 || nY >= pTexture->GetDimension()->m_nHeight)
		return rColor;


	// Lock surface if needed
	if( SDL_MUSTLOCK( pTexture->GetSDLSurface()))
	{
		if( SDL_LockSurface( pTexture->GetSDLSurface()) < 0 )
		{
			fprintf(stderr, "Error - GraphicsObject::GetPixelColorAt: Surface '%s' could not be locked: %s\n", pTexture->GetFilePath().c_str(), SDL_GetError());
			return rColor;
		}
	}

	
	// get bytes per pixel
	Uint32 nPixelValue = 0;
	int bpp = pTexture->GetSDLSurface()->format->BytesPerPixel;

	/* Here p is the address to the pixel we want to retrieve */
	Uint8 *p = (Uint8 *)pTexture->GetSDLSurface()->pixels + nY * pTexture->GetSDLSurface()->pitch + nX * bpp;

	switch(bpp) 
	{
		case 1:
			nPixelValue = *p;
		break;

		case 2:
			nPixelValue = *(Uint16 *)p;
		break;

		case 3:
			if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
				nPixelValue = p[0] << 16 | p[1] << 8 | p[2];
			else
				nPixelValue = p[0] | p[1] << 8 | p[2] << 16;
		break;

		case 4:
			nPixelValue = *(Uint32 *)p;
		break;

		default:
			nPixelValue = 0;
		break;
	}
			
	// Unlock if needed
	if( SDL_MUSTLOCK( pTexture->GetSDLSurface())) 
		SDL_UnlockSurface( pTexture->GetSDLSurface());
		
	SDL_GetRGBA(nPixelValue, pTexture->GetSDLSurface()->format, &rColor.m_nRed, &rColor.m_nGreen, &rColor.m_nBlue, &rColor.m_nAlpha);

	return rColor;

}


void GraphicsObject::SetPixelColorAt(int nX, int nY, ColorRGBA *pColor)
{
	/*switch(GetRenderMode())
	{
		case kRENDER_MODE_SDL:
			SetPixelColorAtSDL(nX, nY, pColor);
		break;

		case kRENDER_MODE_OPENGL:
			SetPixelColorAtOGL(nX, nY, pColor);
		break;
	}*/
}

void GraphicsObject::SetPixelColorAtSDL(int nX, int nY, ColorRGBA *pColor)
{
	if(!GetTexture())
	{
		fprintf(stderr, "Error - GraphicsObject::SetPixelColorAt: No Surface or no SDL surface in surface object: \n");
		return;
	}

	// check if there's any data in our surface
	TextureSDL *pTexture = (TextureSDL*) GetTexture();
	if(!pTexture->GetSDLSurface())
		return;


	if(nX < 0 || nX >= pTexture->GetDimension()->m_nWidth || nY < 0 || nY >= pTexture->GetDimension()->m_nHeight)
		return;
	
	Uint32 nPixelValue = SDL_MapRGBA(pTexture->GetSDLSurface()->format, pColor->m_nRed, pColor->m_nGreen, pColor->m_nBlue, pColor->m_nAlpha);
    
	// Lock surface if needed
	if( SDL_MUSTLOCK( pTexture->GetSDLSurface()))
	{
		if( SDL_LockSurface( pTexture->GetSDLSurface() ) < 0 )
		{
			fprintf(stderr, "Error - GraphicsObject::SetPixelColorAt: Surface '%s' could not be locked: %s\n", pTexture->GetFilePath().c_str(), SDL_GetError());
			return;
		}
	}

	int bpp = pTexture->GetSDLSurface()->format->BytesPerPixel;

    /* Here p is the address to the pixel we want to set */
	Uint8 *p = (Uint8 *)pTexture->GetSDLSurface()->pixels + nY * pTexture->GetSDLSurface()->pitch + nX * bpp;
	
    switch(bpp) 
	{
		case 1:
			*p = (Uint8) nPixelValue;
		break;

		case 2:
			*(Uint16 *)p = (Uint16) nPixelValue;
		break;

		case 3:
			if(SDL_BYTEORDER == SDL_BIG_ENDIAN) 
			{
				p[0] = (nPixelValue >> 16) & 0xff;
				p[1] = (nPixelValue >> 8) & 0xff;
				p[2] = nPixelValue & 0xff;
			} 
			else 
			{
				p[0] = nPixelValue & 0xff;
				p[1] = (nPixelValue >> 8) & 0xff;
				p[2] = (nPixelValue >> 16) & 0xff;
			}
		break;

		case 4:
			*(Uint32 *)p = nPixelValue;
		break;
    }

	// Unlock if needed
	if( SDL_MUSTLOCK( pTexture->GetSDLSurface())) 
		SDL_UnlockSurface( pTexture->GetSDLSurface());
}


void GraphicsObject::SetPixelColorAtOGL(int nX, int nY, ColorRGBA *pColor)
{
	if(!GetTexture())
	{
		fprintf(stderr, "Error - GraphicsObject::SetPixelColorAt: No Surface or no SDL surface in surface object: \n");
		return;
	}

	// check if there's any data in our surface
	TextureOGL *pTexture = (TextureOGL*) GetTexture();
	if(!pTexture->IsEditable() || !pTexture->GetSDLSurface())
		return;


	if(nX < 0 || nX >= pTexture->GetDimension()->m_nWidth || nY < 0 || nY >= pTexture->GetDimension()->m_nHeight)
		return;
	
	Uint32 nPixelValue = SDL_MapRGBA(pTexture->GetSDLSurface()->format, pColor->m_nRed, pColor->m_nGreen, pColor->m_nBlue, pColor->m_nAlpha);
    
	// Lock surface if needed
	if( SDL_MUSTLOCK( pTexture->GetSDLSurface()))
	{
		if( SDL_LockSurface( pTexture->GetSDLSurface() ) < 0 )
		{
			fprintf(stderr, "Error - GraphicsObject::SetPixelColorAt: Surface '%s' could not be locked: %s\n", pTexture->GetFilePath().c_str(), SDL_GetError());
			return;
		}
	}

	int bpp = pTexture->GetSDLSurface()->format->BytesPerPixel;

    /* Here p is the address to the pixel we want to set */
	Uint8 *p = (Uint8 *)pTexture->GetSDLSurface()->pixels + nY * pTexture->GetSDLSurface()->pitch + nX * bpp;
	
    switch(bpp) 
	{
		case 1:
			*p = (Uint8) nPixelValue;
		break;

		case 2:
			*(Uint16 *)p = (Uint16) nPixelValue;
		break;

		case 3:
			if(SDL_BYTEORDER == SDL_BIG_ENDIAN) 
			{
				p[0] = (nPixelValue >> 16) & 0xff;
				p[1] = (nPixelValue >> 8) & 0xff;
				p[2] = nPixelValue & 0xff;
			} 
			else 
			{
				p[0] = nPixelValue & 0xff;
				p[1] = (nPixelValue >> 8) & 0xff;
				p[2] = (nPixelValue >> 16) & 0xff;
			}
		break;

		case 4:
			*(Uint32 *)p = nPixelValue;
		break;
    }

	// Unlock if needed
	if( SDL_MUSTLOCK( pTexture->GetSDLSurface())) 
		SDL_UnlockSurface( pTexture->GetSDLSurface());

	//if(pTexture->RebuildGLTexture())
	//{
		//TODO: Add failure message
	//}
}

bool GraphicsObject::IsVisibleWindow(int nWindowWidth, int nWindowHeight)
{
	if(!IsCullingEnabled())
		return true;

	Vector2D *pPos = GetPos();

	if( (pPos->m_fX >= 0.0f) && (pPos->m_fX <= (float)nWindowWidth) )
	{
		if(	(pPos->m_fY >= 0.0f) && (pPos->m_fY <= (float)nWindowHeight) )
			return true;
	}
	
	return false;
}

void GraphicsObject::SetPresetBlendMode(BLENDMODE_PRESET nBlendMode)
{
	m_nBlendMode = nBlendMode;
	switch(m_nBlendMode)
	{
		case kBLENDMODE_PRESET_ALPHAMASK:
			m_nBlendFactorSrc = BLEND_SRC_ALPHA;
			m_nBlendFactorDest = BLEND_ONE_MINUS_SRC_ALPHA;
		break;

		case kBLENDMODE_PRESET_ADDITIVE:
			m_nBlendFactorSrc = BLEND_ONE;
			m_nBlendFactorDest = BLEND_ONE;
		break;

		default:
			m_nBlendFactorSrc = BLEND_ONE;
			m_nBlendFactorDest = BLEND_ZERO;
		break;
	}
}